package network

import (
	"bufio"
	"bytes"
	"github.com/opentracing/opentracing-go"
	"github.com/opentracing/opentracing-go/ext"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"operator/pkg/format"
	"operator/pkg/tracing"
	"operator/pkg/util"
	"strconv"
	"sync"
)

type InBoundServer struct {
	mu       sync.RWMutex
	list     map[string]*NetStatus
	listener *net.TCPListener
	conn     net.Conn
	trace    tracing.Interface
}

func (ts *InBoundServer) listen() {
	conn, err := ts.listener.Accept()
	if err != nil {
		format.Errors("pbk/network/inbound.go", 32, err.Error())
		return
	}
	ts.mu.Lock()
	defer ts.mu.Unlock()

	//status := NewNetStatus(conn, time.Second*20)
	//ts.list[conn.RemoteAddr().String()] = status

	go ts.dealConn(conn)
}

func (ts *InBoundServer) dealConn(conn net.Conn) {
	buffer := make([]byte, 1024*30)
	for {
		n, err := conn.Read(buffer)
		if err != nil && err != io.EOF {
			format.Errors("pbk/network/inbound.go", 47, err.Error())
			continue
		}
		var span opentracing.Span
		b := bytes.NewBuffer(buffer[:n])
		req, err := http.ReadRequest(bufio.NewReader(b))
		sendBuf := make([]byte, 1024*30)
		sendBuf = buffer[:n]
		if err != nil {
			format.Errors("pbk/network/inbound.go", 58, err.Error())
			continue
		}

		sc, err := ts.trace.Tracer().Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
		if err != nil {
			span = ts.trace.StartSpan(req.URL.Path, nil)
		} else {
			span = ts.trace.StartSpan(req.URL.Path, sc)
		}
		ext.HTTPUrl.Set(span, req.URL.Path)
		ext.HTTPMethod.Set(span, req.Method)
		//fmt.Println(span.Context().(jaeger.SpanContext).TraceID())

		param, _ := ioutil.ReadAll(req.Body)
		span.SetTag("http_body", string(param))
		err = ts.trace.Tracer().Inject(span.Context(), opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
		if err != nil {
			format.Errors("pbk/network/inbound.go", 76, err.Error())
			ext.HTTPStatusCode.Set(span, 500)
			span.SetTag("error", err.Error())
			span.Finish()
			break
		}

		sendBuf = util.RequestParser(req)

		_, err = ts.conn.Write(sendBuf)
		if err != nil {
			format.Errors("pbk/network/inbound.go", 87, err.Error())
			ext.HTTPStatusCode.Set(span, 500)
			span.SetTag("error", err.Error())
			span.Finish()
			break
		}

		cbuf := make([]byte, 20*1024, 20*1024)
		count, err := ts.conn.Read(cbuf)
		if err != nil {
			format.Errors("pbk/network/inbound.go", 97, err.Error())
			ext.HTTPStatusCode.Set(span, 500)
			span.SetTag("error", err.Error())
			span.Finish()
			break
		}

		_, err = conn.Write(cbuf[:count])
		if err != nil {
			format.Errors("pbk/network/inbound.go", 106, err.Error())
			ext.HTTPStatusCode.Set(span, 500)
			span.SetTag("error", err.Error())
			span.Finish()
			break
		}

		if span != nil {
			ext.HTTPStatusCode.Set(span, 200)
			span.SetTag("http_resp", string(cbuf[:count]))
			span.Finish()
		}
	}

}

func (ts *InBoundServer) Run(stopCh <-chan struct{}) {
	for {
		select {
		case <-stopCh:
			break
		default:
			ts.listen()
		}
	}
}

func NewInBoundServer(port int, trace tracing.Interface) (*InBoundServer, error) {
	addr, err := net.ResolveTCPAddr("tcp", ":"+strconv.Itoa(util.SidecarInBoundPort))
	if err != nil {
		format.Errors("pkg/network/inbound.go", 134, err.Error())
		return nil, err
	}
	listener, err := net.ListenTCP("tcp", addr)
	if err != nil {
		format.Errors("pkg/network/inbound.go", 139, err.Error())
		return nil, err
	}

	conn, err := net.Dial("tcp", "127.0.0.1:"+strconv.Itoa(port))
	if err != nil {
		format.Errors("pkg/network/inbound.go", 145, err.Error())
		return nil, err
	}
	tcp := &InBoundServer{
		listener: listener,
		mu:       sync.RWMutex{},
		conn:     conn,
		list:     map[string]*NetStatus{},
		trace:    trace,
	}

	return tcp, nil
}
